/*|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
  |                   Métodos Numéricos II                   |
  |        Problemas de Valor Inicial - Tarefas 2 e 3        |
  |         Método de Forward Euler e Backward Euler         |
  |__________________________________________________________|*/

// É Linux ou é Windows?
#ifdef linux      // Sistema Operacional: Linux.
    #define sistema_operacional 0
#else
    #ifdef _WIN32 // Sistema Operacional: Windows.
        #define sistema_operacional 1
    #endif
#endif
// Vixe! Não é nenhum dos 2...
#ifndef sistema_operacional
    #error Sistema Operacional desconhecido!!! So rodo no Linux ou no Windows!!!
#endif

/*** LIXUX - INCLUDES E DEFINES ***/
#if (sistema_operacional == 0)
    #include "Extras/fuba_linux.h"
#endif

/*** WINDOWS - INCLUDES E DEFINES ***/
#if (sistema_operacional == 1)
    #include "Extras/fuba_win.h"
#endif

/*** DEFINES do programa ***/
#define ARQUIVO_FORWARD_EULER_VELOCIDADE    "fe_velocidade.txt"
#define ARQUIVO_FORWARD_EULER_ESPACO        "fe_espaco.txt"
#define ARQUIVO_BACKWARD_EULER_VELOCIDADE   "be_velocidade.txt"
#define ARQUIVO_BACKWARD_EULER_ESPACO       "be_espaco.txt"

/*** INCLUDES do programa ***/
#include <iostream>
#include <math.h>
using namespace std;

/// Cabeçalhos das funções ///
bool forward_euler(double delta_tempo);
bool backward_euler(double delta_tempo);
double be_velocidade(double Vant, double Vatual, double delta_tempo);
double be_velocidade_derivada(double delta_tempo);
double be_espaco(double Yant, double Vatual, double delta_tempo);
double newton_raphson(double erro1, double erro2, double Vant, double delta_tempo);

/// Dados do problema ///
double Vzero = -2;   // Velocidade inicial.
double Yzero = 2000; // Espaço inicial.
double Ti    = 0;    // Tempo inicial.
double Tf    = 20;   // Tempo final.
double g     = 10;   // Gravidade.
double k     = 50;   // Coeficiente de empuxo.
double m     = 50;   // Massa.
double alfa=k/m;

// Método de Forward Euler:
bool forward_euler(double delta_tempo)
{
    double Vant=Vzero;
    double Yant=Yzero;
    double Vatual, Yatual;

    // Abrindo arquivo onde serão salvos os pontos da função velocidade:
    FILE *arquivo_fe_velocidade = fopen(ARQUIVO_FORWARD_EULER_VELOCIDADE,"w");
    if (arquivo_fe_velocidade == NULL) return(false);

    // Abrindo arquivo onde serão salvos os pontos da função espaço:
    FILE *arquivo_fe_espaco = fopen(ARQUIVO_FORWARD_EULER_ESPACO,"w");
    if (arquivo_fe_espaco == NULL) return(false);

    // Salvando na 1ª linha de cada arquivo o valor do delta tempo usado:
    fprintf(arquivo_fe_velocidade,"# Delta tempo usado: %lf\n",delta_tempo);
    fprintf(arquivo_fe_espaco,"# Delta tempo usado: %lf\n",delta_tempo);

    // Percorrendo o intervalo de tempo:
    for (double i=Ti ; i<=Tf ; i+=delta_tempo)
    {
        // Salvando o ponto (tempo,velocidade) atual:
        fprintf(arquivo_fe_velocidade,"%lf\t%lf\n",i,Vant);
        Vatual = (1 - alfa*delta_tempo)*Vant - (g*delta_tempo);
        // Salvando o ponto (tempo,espaço) atual:
        fprintf(arquivo_fe_espaco,"%lf\t%lf\n",i,Yant);
        Yatual = Yant + delta_tempo*Vant;
        // Atualizando Vant e Yant com os novos valores:
        Vant = Vatual;
        Yant = Yatual;
    }
    fclose(arquivo_fe_velocidade);
    fclose(arquivo_fe_espaco);
    return(true);
}

// Método de Backward Euler:
bool backward_euler(double delta_tempo)
{
    double Vant=Vzero;
    double Yant=Yzero;
    double Vatual, Yatual;
    double erro_1=0.0001;
    double erro_2=0.0001;

    // Abrindo arquivo onde serão salvos os pontos da função velocidade:
    FILE *arquivo_be_velocidade = fopen(ARQUIVO_BACKWARD_EULER_VELOCIDADE,"w");
    if (arquivo_be_velocidade == NULL) return(false);

    // Abrindo arquivo onde serão salvos os pontos da função espaço:
    FILE *arquivo_be_espaco = fopen(ARQUIVO_BACKWARD_EULER_ESPACO,"w");
    if (arquivo_be_espaco == NULL) return(false);

    // Salvando na 1ª linha de cada arquivo o valor do delta tempo usado:
    fprintf(arquivo_be_velocidade,"# Delta tempo usado: %lf\n",delta_tempo);
    fprintf(arquivo_be_espaco,"# Delta tempo usado: %lf\n",delta_tempo);

    // Percorrendo o intervalo de tempo:
    for (double i=Ti ; i<=Tf ; i+=delta_tempo)
    {
        // Salvando o ponto (tempo,velocidade) atual:
        fprintf(arquivo_be_velocidade,"%lf\t%lf\n",i,Vant);
        Vatual = newton_raphson(erro_1,erro_2,Vant,delta_tempo);
        // Salvando o ponto (tempo,espaço) atual:
        fprintf(arquivo_be_espaco,"%lf\t%lf\n",i,Yant);
        Yatual = be_espaco(Yant,Vatual,delta_tempo);
        // Atualizando Vant e Yant com os novos valores:
        Vant = Vatual;
        Yant = Yatual;
    }
    fclose(arquivo_be_velocidade);
    fclose(arquivo_be_espaco);
    return(true);
}

// Fórmula da velocidade para o Backward Euler:
double be_velocidade(double Vant, double Vatual, double delta_tempo)
{
    return((1 + alfa*delta_tempo)*Vatual - Vant + delta_tempo*g);
}

// Fórmula da derivada da velocidade para o Backward Euler:
double be_velocidade_derivada(double delta_tempo)
{
    return(1 + alfa*delta_tempo);
}

// Fórmula do espaço para o Backward Euler:
double be_espaco(double Yant, double Vatual, double delta_tempo)
{
    return(Yant + delta_tempo*Vatual);
}

// Método de Newton-Raphson:
double newton_raphson(double erro1, double erro2, double Vant, double delta_tempo)
{
    double x_k = Vant;
    double x_k_prox;
    while(1)
    {
        // x_k_prox = x_k - (fx / f'x):
        x_k_prox = x_k - (be_velocidade(Vant,x_k,delta_tempo) / be_velocidade_derivada(delta_tempo));
        // |f(x_k)| <= erro1:
        if (fabs(be_velocidade(Vant,x_k,delta_tempo)) <= erro1) break;
        // |x_k_prox - x_k| <= erro2:
        if (fabs(x_k_prox - x_k) <= erro2) break;
        x_k = x_k_prox;
    }
    return(x_k_prox);
}

int main(void) // PRINCIPAL //
{
    // Variáveis do MAIN:
    char escolha = '\0';
    double delta_tempo=0.5;

    do{// MENU PRINCIPAL //
        limpar_tela();
        cout <<"#=============================================================#"<<endl;
        cout <<"#         Problemas de Valor Inicial - Tarefas 2 e 3          #"<<endl;
		cout <<"#          Metodo de Forward Euler e Backward Euler           #"<<endl;
        cout <<"#=============================================================#"<<endl;
        cout <<"> Valor do delta tempo: "<<delta_tempo<<" segundo(s)."<<endl;
        cout <<"> Escolha:"<<endl;
        cout <<"  (1) Inserir o delta tempo;"<<endl;
        cout <<"  (2) Metodo de Forward Euler;"<<endl;
        cout <<"  (3) Metodo de Backward Euler;"<<endl;
        cout <<"  (X) Sair do programa."<<endl;
        escolha = ler_tecla();
        switch(escolha)
        {
            case '1': /// Inserir o delta tempo ///
            {
                limpar_tela();
                cout <<"+---------------+"<<endl;
                cout <<"|  Delta tempo  |"<<endl;
                cout <<"+---------------+"<<endl;
                cout <<"* Digite o valor do delta tempo: ";
                cin >> delta_tempo;
                break;
            }

            case '2': /// Método de Forward Euler ///
            {
                limpar_tela();
                cout <<"+---------------------------+"<<endl;
                cout <<"|  Metodo de Forward Euler  |"<<endl;
                cout <<"+---------------------------+"<<endl;
                if (forward_euler(delta_tempo))
                {
                    cout <<"* Os valores de tempo, velocidade e espaco foram salvos nos arquivos:"<<endl;
                    cout <<"  "<<ARQUIVO_FORWARD_EULER_VELOCIDADE<<" e "<<ARQUIVO_FORWARD_EULER_ESPACO<<endl;
                }
                else
                    cout <<"* Os arquivos nao puderam ser abertos"<<endl;
                cout<<endl;
                pausar();
                break;
            }

            case '3': /// Método de Backward Euler ///
            {
                limpar_tela();
                cout <<"+----------------------------+"<<endl;
                cout <<"|  Metodo de Backward Euler  |"<<endl;
                cout <<"+----------------------------+"<<endl;
                if (backward_euler(delta_tempo))
                {
                    cout <<"* Os valores de tempo, velocidade e espaco foram salvos nos arquivos:"<<endl;
                    cout <<"  "<<ARQUIVO_BACKWARD_EULER_VELOCIDADE<<" e "<<ARQUIVO_BACKWARD_EULER_ESPACO<<endl;
                }
                else
                    cout <<"* Os arquivos nao puderam ser abertos"<<endl;
                cout<<endl;
                pausar();
                break;
            }
        }// fim do "switch(escolha)" //
      }while(escolha!='X');// fim do "MENU PRINCIPAL" //
    return(0);
}
